import pandas as pd
from pandas import Series, DataFrame
import numpy as np


def create_df(index=[], columns=[]):
    df = DataFrame({column: [column + str(i) for i in index] for column in columns})
    df.index = index
    return df


df = create_df([1, 2, 3, 4], list('ABCD'))
df.loc[2] = df.loc[1]
print(df)

# 检查重复行，返回bool
print(df.duplicated())
print(df.duplicated(subset=['A', 'B', 'C']))  # 指定子集进行去重
# 删除重复行
print(df.drop_duplicates())
print(df[~df.duplicated()])  # 同上
print(df[np.logical_not(df.duplicated())])  # 同上
print(df.drop_duplicates(subset=['A', 'B', 'C']))  # 指定子集进行去重

# 映射
## 1.replace()函数：替换元素
data = np.random.randint(0, 150, size=(4, 4))
index = ['张三', '李四', '王五', '赵六']
columns = ['语文', '数学', '英语', 'python']
df = DataFrame(data, index, columns)
print(df)
### 首先定义一个字典
mapping = {k: k + 70 for k in np.sort(df.values.reshape(-1))[:4]}
print(mapping)
print(df.replace(mapping))
### 经常使用来替换NaN元素
df.iloc[0, 0] = np.nan
df.replace({np.nan: 0}, inplace=True)
print(df)
## 2.最重要map()函数：由已有的列生成新列
### 2.1 生成新列
df['理综'] = df['数学'].map({i: i + 100 for i in df['数学']})
print(df)
### 2.2 修改已有列的数据
df['数学'] = df['数学'].map({i: i + 0.5 for i in df['数学']})
### 2.3 map()函数中使用lambda函数
df['是否及格'] = (df.sum(axis=1) - df['理综']).map(lambda x: '及格' if x > 240 else '不及格')
print(df)


### 2.4 map()函数中使用普通函数
def convert(item):
    if item >= 60:
        return '优秀'
    elif item >= 80:
        return '及格'
    else:
        return '不及格'


df['英语是否及格'] = df['英语'].map(convert)
print(df)
### transform()和map()类似
df['英语是否及格1'] = df['英语'].transform(convert)
print(df)
## 3.rename()函数：替换索引
maping_name = {'语文': 'Chinese', '数学': 'Math'}
df.rename(maping_name, axis=1, inplace=True)
print(df)
df.rename(columns=mapping, inplace=True)  # 同上
df.rename(index={'张三': 'Mr Zhang'}, inplace=True)
print(df)

# 异常值检测和过滤
print(df.describe())
"""新建一个形状为10000*3的标准正态分布的DataFrame(np.random.randn)，
去掉所有满足以下情况的行：
1.其中任一列元素绝对值大于3倍这一列的标准差"""
df1 = DataFrame(data=np.random.randn(10000, 3))
print(df1[~(df1.abs() > 3 * df1.std()).any(axis=1)])
## df1.abs() > 3 * df1.std() 进行比较是会进行广播，df1.std()是生成df1的三列的标准差
# 是否是广播还是对应索引计算
"""
方法论：
1.指定异常值检测的标准（绝对值大于3倍这一列的标准差）
2.把标准写成一个条件（df1.abs() > 3 * df1.std()）
3.再看过滤行还是列（(df1.abs() > 3 * df1.std()).any(axis=1)）
4.最后通过中括号中写条件写法把异常值过滤
"""
